package SecureChat;		
		
		
import java.util.Arrays;		
import java.math.BigInteger;		
import java.security.SecureRandom;		
import java.security.MessageDigest;		
import javax.crypto.Cipher;		
import javax.crypto.spec.SecretKeySpec;		
import javax.crypto.spec.IvParameterSpec;	

public class Utilities {
    
    private static final BigInteger ONE = new BigInteger("1");
    
        public static BigInteger GenRandNum(){
                //Return 128bit BigInteger Random Number
                SecureRandom rand = new SecureRandom();
                BigInteger num = new BigInteger(128, rand) ;
                
                return num;
        }
        
        
        public static BigInteger GenRandPrime(){
                //Return 128bit BigInteger Random Prime Number               
                BigInteger prime;
                boolean m;
                do {
                    SecureRandom rand = new SecureRandom();
                    prime = new BigInteger(128, 5, rand) ;
                    //This constructor which makes probable prime could be wrong
                    //Miller Rabin test is a safty lock
                    m= Miller_Rabin.is_prime(prime,10);
                } while (!m);

                return prime;
        }
            
        
        public static byte[] HashValues(BigInteger key, String id) throws Exception
        {
                //Using SHA-1
                //Hash : HASH(ID || KEY)

                MessageDigest md = MessageDigest.getInstance("SHA-1");
                md.reset();
                
                byte[] ID = id.getBytes();
                byte[] KEY = key.toByteArray();
                
                //Concetnate Bytes ID and KEY
                md.update(ID);
                md.update(KEY);
                
                //Hash
                byte[] hash = md.digest();

                return hash;
        }
        
		public static byte[] Hash(String hashme) throws Exception
        {
                //Using SHA-1
                //Hash : HASH(ID || KEY)

                MessageDigest md = MessageDigest.getInstance("SHA-1");
                md.reset();
                
                //Concetnate Bytes ID and KEY
                md.update(hashme);
                
                //Hash
                byte[] hash = md.digest();

                return hash;
        }
        
        public static BigInteger CalcModPower(BigInteger g, BigInteger exp, BigInteger p)
        {
                // Fast modula exponentiation for Key exchange.
                // From Right-to-left binary method @ Wikipedia
                // http://en.wikipedia.org/wiki/Modular_exponentiation
                BigInteger num = ONE;
                String bin = exp.toString(2);   // int to binary
                
                for(int i = 0; i < bin.length(); i++)   
                {
                    num = num.pow(2).mod(p);    // g^2 mod p

                    if(bin.charAt(i) == '1')
                        num = num.multiply(g).mod(p);   // g*a mod p 
                }
                
                return num;
        }
        
        
        public static byte[] GenIV() throws Exception
        {
                //Just create a random byte array
                MessageDigest md = MessageDigest.getInstance("MD5");

                SecureRandom rand = new SecureRandom();
                BigInteger num = new BigInteger(128, rand) ;
                
                byte[] buffer = num.toByteArray();
                
                md.reset();
                md.update(buffer);
                               
                byte[] iv = md.digest();
               
                return iv;
        }
        
        
//2.1.1.  Generation of ZZ
//
//   X9.42 defines that the shared secret ZZ is generated as follows:
//
//     ZZ = g ^ (xb * xa) mod p
//
//   Note that the individual parties actually perform the computations:
//
//     ZZ = (yb ^ xa)  mod p  = (ya ^ xb)  mod p
//
//   where ^ denotes exponentiation
//
//         ya is party a's public key; ya = g ^ xa mod p
//         yb is party b's public key; yb = g ^ xb mod p
//         xa is party a's private key
//         xb is party b's private key
//         p is a large prime
//         q is a large prime
//         g = h^{(p-1)/q} mod p, where
//         h is any integer with 1 < h < p-1 such that h{(p-1)/q} mod p > 1
//           (g has order q mod p; i.e. g^q mod p = 1 if g!=1)
//         j a large integer such that p=qj + 1
//         (See Section 2.2 for criteria for keys and parameters)
//

        public static BigInteger GenGG(BigInteger p) throws Exception
        {
            BigInteger e,g,h;
            boolean m;
            int n;
                
            do{
                //BigInteger p = Utilities.GenRandPrime();
                BigInteger q = Utilities.GenRandPrime();
                BigInteger t = p.subtract(ONE);
                do {                    
                    do{
                        h = Utilities.GenRandPrime();    
                        n = h.compareTo(t);
                       } while(n==-1);

                    //System.out.println(q);
                    m = p.equals(q);
                    
                } while (m);
                
                e = t.divide(q);
                g = Utilities.CalcModPower(h,e,p);
                      
            }while(g.equals(ONE));
            
            return g;
        }        
        
        public static BigInteger GenG(String passwd) throws Exception
        {
                //Generate common value for g
                MessageDigest md = MessageDigest.getInstance("SHA-1");
                
                byte[] PW = passwd.getBytes();
                
                md.reset();
                md.update(PW); 
                byte[] G = md.digest();
                               
                BigInteger g = new BigInteger(G);
                return g;
        }
      

        
        public static String Encrypt(byte[] key, byte[] iv, String plainText) throws Exception
        {
                // Encrypt plaintext to ciphertext

                // Convert plaintext to byte format
                byte[] toEncrypt = plainText.getBytes("UTF-8");       
                
                // Use first 128bit length as key
                key = Arrays.copyOf(key, 16); 
                
                // Generate the secret key specs
                SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
                IvParameterSpec ivSpec = new IvParameterSpec(iv);
                
                // Instantiate the cipher
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);

                byte[] cipherText = cipher.doFinal(toEncrypt); 
                
                return Convert.toHex(cipherText);
        }
        
        
        public static String Decrypt(byte[] key, byte[] iv, String cipherText) throws Exception
        {
                // Convert ciphertext to string format
                byte[] toDecrypt = Convert.toByte(cipherText);    

                // Use first 128bit length as key
                key = Arrays.copyOf(key, 16); 
                
                // Generate the secret key specs
                SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
                IvParameterSpec ivSpec = new IvParameterSpec(iv);
                
                // Instantiate the cipher
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);

                byte[] plainText = cipher.doFinal(toDecrypt);
                
                String originalString = new String(plainText, "UTF-8");  
                
                return originalString;
        }
}